package com.sayyi.demo.bean.copy.jmh;

import com.github.javafaker.Faker;
import com.sayyi.demo.bean.domain.Dest;
import com.sayyi.demo.bean.domain.Source;
import ma.glasnost.orika.BoundMapperFacade;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import net.sf.cglib.beans.BeanCopier;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.util.concurrent.TimeUnit;

/**
 * @author xuchuang
 * date 2021/12/17 6:15 PM
 */
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class BeanCopyJmh {

    private Faker faker = new Faker();
    // orika
    private DefaultMapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    private MapperFacade mapper;
    private BoundMapperFacade<Source, Dest> bound;
    // cglib
    private BeanCopier beanCopier;
    // mapstruct
    SourceMapper sourceMapper = SourceMapper.INSTANCE;

    private Source source;

    @Setup
    public void prepare() {
        source = new Source();
        source.setName("hello");
        source.setAge(21);
        source.setVar1(faker.regexify("\\w[7]"));
        source.setVar2(faker.regexify("\\w[7]"));
        source.setVar3(faker.regexify("\\w[7]"));
        source.setVar4(faker.regexify("\\w[7]"));
        source.setVar5(faker.regexify("\\w[7]"));
        source.setVar6(faker.regexify("\\w[7]"));
        source.setVar7(faker.regexify("\\w[7]"));
        source.setVar8(faker.regexify("\\w[7]"));
        source.setVar9(faker.regexify("\\w[7]"));
        source.setVar10(faker.regexify("\\w[7]"));

        bound = mapperFactory.getMapperFacade(Source.class, Dest.class);

        mapperFactory.classMap(Source.class, Dest.class).byDefault().register();
        mapper = mapperFactory.getMapperFacade();
        beanCopier = BeanCopier.create(Source.class, Dest.class, false);
    }

    /**
     * 秒 毫秒 微秒 纳秒
     * 直接复制：345ns
     * @return
     */
    @Benchmark
    public Dest mapper() {
        return mapper.map(source, Dest.class);
    }

    /**
     * 缓存复制：70ns
     * @return
     */
    @Benchmark
    public Dest classMap() {
        return bound.map(source);
    }

    /**
     * 手动复制：25ns
     * @return
     */
    @Benchmark
    public Dest manual() {
        Dest dest = new Dest();
        dest.setName(source.getName());
        dest.setAge(source.getAge() + "");
        dest.setVar1(source.getVar1());
        dest.setVar2(source.getVar2());
        dest.setVar3(source.getVar3());
        dest.setVar4(source.getVar4());
        dest.setVar5(source.getVar5());
        dest.setVar6(source.getVar6());
        dest.setVar7(source.getVar7());
        dest.setVar8(source.getVar8());
        dest.setVar9(source.getVar9());
        dest.setVar10(source.getVar10());
        return dest;
    }

    /**
     * cglib 不同类型的属性，默认情况下不会进行映射。不会处理int to string，所以快一些
     * @return
     */
    @Benchmark
    public Dest cglib() {
        Dest dest = new Dest();
        beanCopier.copy(source, dest, null);
        return dest;
    }

    /**
     * mapstruct，生成代码处理
     * @return
     */
    @Benchmark
    public Dest mapstruct() {
        return sourceMapper.sourceToDest(source);
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(BeanCopyJmh.class.getSimpleName())
                .forks(1)
                .warmupIterations(1)
                .warmupTime(TimeValue.seconds(5))
                .measurementIterations(3)
                .measurementTime(TimeValue.seconds(5))
                .build();

        new Runner(opt).run();
    }
}
